/*
 * @Date: 2025-09-19 17:40:53
 * @LastEditors: lixu lixu@puchigames.com
 * @LastEditTime: 2025-09-19 17:40:55
 * @FilePath: /ix-redis/basic.go
 * @Description:
 */
package ixRedis

import (
	"encoding/json"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

// Ping 测试连接
func (c *Client) Ping() error {
	conn, err := c.getConn()
	if err != nil {
		return err
	}
	defer conn.Close()

	_, err = conn.Do("PING")
	return err
}

// Set 设置键值（支持任何类型）
func (c *Client) Set(key string, value interface{}) error {
	return c.SetWithExpire(key, value, 0)
}

// SetWithExpire 设置键值并指定过期时间（支持任何类型）
func (c *Client) SetWithExpire(key string, value interface{}, expiration time.Duration) error {
	conn, err := c.getConn()
	if err != nil {
		return err
	}
	defer conn.Close()

	// 构建带前缀的键
	fullKey := c.buildKey(key)

	// 将值转换为字符串存储
	var strValue string
	switch v := value.(type) {
	case string:
		strValue = v
	case int:
		strValue = strconv.FormatInt(int64(v), 10)
	case int8:
		strValue = strconv.FormatInt(int64(v), 10)
	case int16:
		strValue = strconv.FormatInt(int64(v), 10)
	case int32:
		strValue = strconv.FormatInt(int64(v), 10)
	case int64:
		strValue = strconv.FormatInt(v, 10)
	case uint:
		strValue = strconv.FormatUint(uint64(v), 10)
	case uint8:
		strValue = strconv.FormatUint(uint64(v), 10)
	case uint16:
		strValue = strconv.FormatUint(uint64(v), 10)
	case uint32:
		strValue = strconv.FormatUint(uint64(v), 10)
	case uint64:
		strValue = strconv.FormatUint(v, 10)
	case float32:
		strValue = strconv.FormatFloat(float64(v), 'f', -1, 32)
	case float64:
		strValue = strconv.FormatFloat(v, 'f', -1, 64)
	case bool:
		strValue = strconv.FormatBool(v)
	case []byte:
		strValue = string(v)
	case nil:
		strValue = ""
	default:
		// 对于复杂类型（结构体、map、slice等），使用JSON序列化
		jsonData, err := json.Marshal(value)
		if err != nil {
			return err
		}
		strValue = string(jsonData)
	}

	if expiration > 0 {
		_, err = conn.Do("SET", fullKey, strValue, "EX", int(expiration.Seconds()))
	} else {
		_, err = conn.Do("SET", fullKey, strValue)
	}
	return err
}

// Get 获取键值并解析到指针参数，返回键是否存在
func (c *Client) Get(key string, dest interface{}) (bool, error) {
	conn, err := c.getConn()
	if err != nil {
		return false, err
	}
	defer conn.Close()

	// 构建带前缀的键
	fullKey := c.buildKey(key)
	reply, err := redis.String(conn.Do("GET", fullKey))
	if err == redis.ErrNil {
		// 键不存在，根据目标类型设置零值
		c.setZeroValue(dest)
		return false, nil
	}
	if err != nil {
		return false, err
	}

	// 如果目标参数是字符串指针，直接赋值
	if strPtr, ok := dest.(*string); ok {
		*strPtr = reply
		return true, nil
	}

	// 对于其他类型，尝试解析
	err = c.parseValue(reply, dest)
	return true, err
}

// GetString 获取键值（返回字符串，兼容旧版本）
func (c *Client) GetString(key string) (string, error) {
	var result string
	_, err := c.Get(key, &result)
	return result, err
}

// parseValue 解析值到目标类型
func (c *Client) parseValue(value string, dest interface{}) error {
	if value == "" {
		return c.setZeroValue(dest)
	}

	// 根据目标类型进行解析
	switch ptr := dest.(type) {
	case *int:
		val, err := strconv.Atoi(value)
		if err != nil {
			return err
		}
		*ptr = val
	case *int8:
		val, err := strconv.ParseInt(value, 10, 8)
		if err != nil {
			return err
		}
		*ptr = int8(val)
	case *int16:
		val, err := strconv.ParseInt(value, 10, 16)
		if err != nil {
			return err
		}
		*ptr = int16(val)
	case *int32:
		val, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return err
		}
		*ptr = int32(val)
	case *int64:
		val, err := strconv.ParseInt(value, 10, 64)
		if err != nil {
			return err
		}
		*ptr = val
	case *uint:
		val, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			return err
		}
		*ptr = uint(val)
	case *uint8:
		val, err := strconv.ParseUint(value, 10, 8)
		if err != nil {
			return err
		}
		*ptr = uint8(val)
	case *uint16:
		val, err := strconv.ParseUint(value, 10, 16)
		if err != nil {
			return err
		}
		*ptr = uint16(val)
	case *uint32:
		val, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			return err
		}
		*ptr = uint32(val)
	case *uint64:
		val, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			return err
		}
		*ptr = val
	case *float32:
		val, err := strconv.ParseFloat(value, 32)
		if err != nil {
			return err
		}
		*ptr = float32(val)
	case *float64:
		val, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		*ptr = val
	case *bool:
		val, err := strconv.ParseBool(value)
		if err != nil {
			return err
		}
		*ptr = val
	case *[]byte:
		*ptr = []byte(value)
	default:
		// 对于复杂类型，使用JSON反序列化
		return json.Unmarshal([]byte(value), dest)
	}

	return nil
}

// setZeroValue 设置目标类型的零值
func (c *Client) setZeroValue(dest interface{}) error {
	switch ptr := dest.(type) {
	case *string:
		*ptr = ""
	case *int, *int8, *int16, *int32, *int64:
		// 整数类型已经是零值
	case *uint, *uint8, *uint16, *uint32, *uint64:
		// 无符号整数类型已经是零值
	case *float32, *float64:
		// 浮点数类型已经是零值
	case *bool:
		*ptr = false
	case *[]byte:
		*ptr = nil
	default:
		// 对于复杂类型，不需要特殊处理，已经是零值
	}
	return nil
}

// GetBytes 获取键值（字节数组）
func (c *Client) GetBytes(key string) ([]byte, error) {
	conn, err := c.getConn()
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	// 构建带前缀的键
	fullKey := c.buildKey(key)
	reply, err := redis.Bytes(conn.Do("GET", fullKey))
	if err == redis.ErrNil {
		return nil, nil
	}
	return reply, err
}

// Del 删除键
func (c *Client) Del(keys ...string) (int64, error) {
	if len(keys) == 0 {
		return 0, nil
	}

	conn, err := c.getConn()
	if err != nil {
		return 0, err
	}
	defer conn.Close()

	args := make([]interface{}, len(keys))
	for i, key := range keys {
		args[i] = c.buildKey(key)
	}

	return redis.Int64(conn.Do("DEL", args...))
}

// Exists 检查键是否存在
func (c *Client) Exists(key string) (bool, error) {
	conn, err := c.getConn()
	if err != nil {
		return false, err
	}
	defer conn.Close()

	// 构建带前缀的键
	fullKey := c.buildKey(key)
	count, err := redis.Int64(conn.Do("EXISTS", fullKey))
	return count > 0, err
}

// Expire 设置键过期时间
func (c *Client) Expire(key string, expiration time.Duration) error {
	conn, err := c.getConn()
	if err != nil {
		return err
	}
	defer conn.Close()

	// 构建带前缀的键
	fullKey := c.buildKey(key)
	_, err = conn.Do("EXPIRE", fullKey, int(expiration.Seconds()))
	return err
}

// TTL 获取键剩余生存时间
func (c *Client) TTL(key string) (time.Duration, error) {
	conn, err := c.getConn()
	if err != nil {
		return 0, err
	}
	defer conn.Close()

	// 构建带前缀的键
	fullKey := c.buildKey(key)
	seconds, err := redis.Int64(conn.Do("TTL", fullKey))
	if err != nil {
		return 0, err
	}

	if seconds == -1 {
		return -1, nil // 键存在但没有设置过期时间
	}
	if seconds == -2 {
		return -2, nil // 键不存在
	}

	return time.Duration(seconds) * time.Second, nil
}
